<?php

namespace pmvc\mvc\annotation;


use pmvc\log\Log;

use pmvc\ioc\Ordered;

use pmvc\annotation\AnnotationUtil;
use pmvc\util\ClassUtil;
use pmvc\util\ReflectionUtil;

use pmvc\mvc\RequestChain;
use pmvc\mvc\Request;
use pmvc\mvc\Response;

use ReflectionClass;
use ReflectionMethod;
use ReflectionObject;

/**
 * A {@link RequestFilter} that uses an annotated class.
 */
class AnnotatedRequestFilter
	implements \pmvc\mvc\RequestFilter,
	Ordered {

	/**
	 * @var ReflectionMethod
	 */
	private $method;

	/**
	 * @var mixed
	 */
	private $requestFilter;

	/**
	 * @var int
	 */
	private $order;

	/**
	 * Creates the class.
	 * @param mixed $requestFilter
	 * @param ReflectionMethod $method
	 */
	public function __construct($requestFilter, ReflectionMethod $method, $order=Ordered::LOWEST_PRECEDENCE) {
		$this->method			= $method;
		$this->requestFilter	= $requestFilter;
		$this->order			= $order;
	}

	
	/**
	 * {@inheritDoc}
	 */
	public function getOrder() {
		return $this->order;
	}
		
	/**
	 * {@inheritDoc}
	 */
    public function handleRequest(RequestChain $chain, Request $request, Response $response) {

    	// get invocation arguments
    	$args = $this->getInvocationArguments($this->method, $request, $response, $chain);

    	// see if we have a RequestChain
    	$hasRequestChain = false;
    	foreach ($args as &$arg) {
    		if (ReflectionUtil::isInstance($arg, 'pmvc\mvc\RequestChain')) {
    			$hasRequestChain = true;
    			break;
    		}
    	}

    	// invoke
    	Log::trace(__CLASS__, "Invoking ".get_class($this->requestFilter)."::".$this->method->getName()." as RequestFilter");
    	$this->method->invokeArgs($this->requestFilter, $args);

    	// call next if we need to
    	if (!$hasRequestChain) {
    		$chain->next($request, $response);
    	}
    }

    /**
     * Returns invocation arguments in an array suitable
     * for calling {@link invokeArgs()} with on the given
     * method.
     * 
     * @param ReflectionMethod $method
     * @param Request $request
     * @param Response $response
     * @param RequestChain $chain
     * @return Array
     */
    private function getInvocationArguments(
    	ReflectionMethod $method, Request $request, Response $response,
    	RequestChain $chain=null) {

    	// get method parameters
    	$methodParams = $method->getParameters();

    	// build argument list
    	$args = Array();
    	foreach ($methodParams as $methodParam) {
    		$name 		= $methodParam->getName();
    		$class 		= $methodParam->getClass();
    		$isArray 	= $methodParam->isArray();
    		$className = (isset($class)) ? $class->getName() : null;
  
    		// Request
    		if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Request')) {
    			$args []= $request;

    		// Response
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Response')) {
    			$args []= $response;

    		// Model
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Model')) {
    			$args []= $response->getModel();

    		// Dispatcher
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\Dispatcher')) {
    			$args []= $request->getDispatcher();

    		// RequestChain
    		} else if (isset($class)
    			&& ReflectionUtil::classIsA($className, 'pmvc\mvc\RequestChain')) {
    			$args []= $chain;
    			
    		// path variables
    		} else if (!isset($class) && $request->hasPathVariable($name)) {
    			$args []= $request->getPathVariable($name.($isArray?"[]":""));
    			
    		// request parameters
    		} else if (!isset($class) && $request->hasParameter($name)) {
    			$args []= $request->getParameter($name.($isArray?"[]":""));

    		// default value
    		} else if ($methodParam->isDefaultValueAvailable()) {
    			$args []= $methodParam->getDefaultValue();

    		// unknown parameter
    		} else {
    			$args []= null;
    			
    		}
    	}
 
    	// return it
    	return $args;
    }
}

?>